#pragma once

#include "FragmentBuilder.h"
#include <mfapi.h>

namespace Microsoft {
	namespace Media {
		namespace FragmentedMediaSink {

			class ISampleProcessor
			{
			private:
				std::vector<FragmentBuilder> _fragmentBuilders;

			protected:
				virtual HRESULT InitializeFragments(unsigned int fragmentIndex)
				{
					HRESULT hr = S_OK;

					for (std::vector<FragmentBuilder>::iterator it = _fragmentBuilders.begin(); it != _fragmentBuilders.end(); ++it)
					{
						hr = it->InitializeFragment(fragmentIndex);
						if (FAILED(hr)) break;
					}

					return hr;
				}

				virtual HRESULT TerminateFragments(long long *fragmentStartTime, long long *fragmentEndTime)
				{
					HRESULT hr = S_OK;

					bool isFirst = true;
					long long adjustedFragmentStartTime = (*fragmentStartTime);
					long long adjustedFragmentEndTime = (*fragmentEndTime);

					for (std::vector<FragmentBuilder>::iterator it = _fragmentBuilders.begin(); it != _fragmentBuilders.end(); ++it)
					{
						long long startTime = (*fragmentStartTime);
						long long endTime = (*fragmentEndTime);

						HRESULT hr = it->TerminateFragment(&startTime, &endTime);

						if (isFirst)
						{
							adjustedFragmentStartTime = startTime;
							adjustedFragmentEndTime = endTime;
							isFirst = false;
						}
						else if (adjustedFragmentEndTime != endTime || adjustedFragmentStartTime != startTime) // ensure that all subsequent times are the same.
						{
							hr = E_UNEXPECTED;
						}
						if (FAILED(hr)) break;
					}

					(*fragmentStartTime) = adjustedFragmentStartTime;
					(*fragmentEndTime) = adjustedFragmentEndTime;

					return hr;
				}

				void Initialize(Windows::Foundation::Collections::IVector<Microsoft::Media::FragmentedMediaSink::IFragmentController^>^ fragmentControllers, IMFMediaType* spInputMediaType, unsigned int inputStreamIndex)
				{
					for each (auto fragmentController in fragmentControllers)
					{
						_fragmentBuilders.push_back(FragmentBuilder(fragmentController, spInputMediaType, inputStreamIndex));
					}
				}

			public:
				ISampleProcessor()
				{ }

				ISampleProcessor(Windows::Foundation::Collections::IVector<Microsoft::Media::FragmentedMediaSink::IFragmentController^>^ fragmentControllers, IMFMediaType* spInputMediaType, unsigned int inputStreamIndex)
				{
					Initialize(fragmentControllers, spInputMediaType, inputStreamIndex);
				}

				virtual ~ISampleProcessor() {}

				virtual HRESULT ProcessSample(IMFSample* spSample)
				{
					HRESULT hr = S_OK;

					for (std::vector<FragmentBuilder>::iterator it = _fragmentBuilders.begin(); it != _fragmentBuilders.end(); ++it)
					{
						hr = it->WriteSample(spSample);
						if (FAILED(hr)) break;
					}

					return hr;
				}

				virtual HRESULT SendTick(long long timeStamp)
				{
					HRESULT hr = S_OK;

					for (std::vector<FragmentBuilder>::iterator it = _fragmentBuilders.begin(); it != _fragmentBuilders.end(); ++it)
					{
						hr = it->SendTick(timeStamp);
						if (FAILED(hr)) break;
					}

					return hr;
				}

				virtual HRESULT Flush()
				{
					HRESULT hr = S_OK;

					for (std::vector<FragmentBuilder>::iterator it = _fragmentBuilders.begin(); it != _fragmentBuilders.end(); ++it)
					{
						hr = it->Flush();
						if (FAILED(hr)) break;
					}

					return hr;
				}

				virtual HRESULT Finalize() = 0;
			};

		}
	}
}